#include "common.h"
#include <pthread.h>

#define MAX_LINE 4096

char rot13_char(char c)
{
    if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
        return c + 13;
    else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
        return c - 13;
    else
        return c;
}

// 通过数组实现队列，由队头、队尾两个指针维护队列
// （1）队列初始化时，front和rear值都为零；
// （2）当队列不为空时，front指向队列的第一个元素，rear指向队列最后一个元素的下一个位置；
// （3）当队列为空时，front与rear的值相等，但不一定为零；
// 这个循环队列实现应该是有 BUG 的，但也无所谓了～
typedef struct
{
    int number;            // 队列描述符最大个数
    int *fd;               // 队列数组
    int front;             // 队头
    int rear;              // 队尾
    pthread_mutex_t mutex; // 锁
    pthread_cond_t cond;   // 条件变量
} block_queue;

// 初始化队列
void block_queue_init(block_queue *queue, int number)
{
    queue->number = number;
    queue->fd = calloc(number, sizeof(int)); // 初始化为 0
    queue->front = queue->rear = 0;          // 队头对尾全是 0
    pthread_mutex_init(&queue->mutex, NULL); // 初始化锁
    pthread_cond_init(&queue->cond, NULL);   // 初始化条件变量
}

// 队列 push
void block_queue_push(block_queue *queue, int fd)
{
    // 加锁
    pthread_mutex_lock(&queue->mutex);
    // 将 fd 加入到队尾
    queue->fd[queue->rear] = fd;
    // 如果已经到达了最后，则重新从 0 开始，循环队列
    // 循环队列最后一个元素不使用
    if (++queue->rear == queue->number)
    {
        queue->rear = 0;
    }
    printf("push fd %d\n", fd);
    // 通知其它线程，fd 来了
    pthread_cond_signal(&queue->cond);
    // 解锁
    pthread_mutex_unlock(&queue->mutex);
}

// 队列 pop
int block_queue_pop(block_queue *queue)
{
    // 加锁
    pthread_mutex_lock(&queue->mutex);
    // 队列中没有新的fd，所以等待
    while (queue->front == queue->rear)
    {
        pthread_cond_wait(&queue->cond, &queue->mutex);
    }
    // 取出队头
    int fd = queue->fd[queue->front];
    // 如果队头到了末尾，则重置从 0 开始，数组最后一个元素不使用
    if (++queue->front == queue->number)
    {
        queue->front = 0; // 置 0
    }
    printf("pop fd %d\n", fd);
    // 解锁
    pthread_mutex_unlock(&queue->mutex);
    return fd;
}

void *thread_run(void *arg)
{
    pthread_t tid = pthread_self();
    pthread_detach(tid);

    block_queue *queue = (block_queue *)arg;
    while (1)
    {
        int fd = block_queue_pop(queue);
        printf("get fd in thread, fd==%d, tid == %ld\n", fd, tid);
        char outbuf[MAX_LINE + 1];
        size_t outbuf_used = 0;
        ssize_t result;
        while (1)
        {
            char ch;
            result = recv(fd, &ch, 1, 0); // 每次接受一个字符
            if (result == 0)
            {
                break; // 没有数据了，直接 break
            }
            else if (result == -1)
            {
                perror("read");
                break; // 读取失败，直接 break
            }
            if (outbuf_used < sizeof(outbuf))
            {
                outbuf[outbuf_used++] = rot13_char(ch);
            }
            // 遇到换行
            // 读取客户端发送的数据，待读完一行后，再向客户端发送
            if (ch == '\n')
            {
                send(fd, outbuf, outbuf_used, 0); // 发送数据
                outbuf_used = 0;
                continue;
            }
        }
    }
}

int main(int argc, char const *argv[])
{
    // 初始化 server
    int port = 3000;
    int listener_fd = tcp_server_listen(port);
    printf("listening at localhost:%d\n", port);

    // 初始化等待队列
    block_queue queue;
    block_queue_init(&queue, 128);

    int thread_num = 8;
    pthread_t *thread_array = calloc(thread_num, sizeof(pthread_t));
    int i;
    for (i = 0; i < thread_num; i++)
    {
        pthread_create(&(thread_array[i]), NULL, &thread_run, (void *)&queue);
    }

    while (1)
    {
        struct sockaddr_storage ss;
        socklen_t slen = sizeof(ss);
        int fd = accept(listener_fd, (struct sockaddr *)&ss, &slen);
        if (fd < 0)
        {
            error(1, errno, "accept failed");
        }
        else
        {
            block_queue_push(&queue, fd);
        }
    }
    return 0;
}
